<!DOCTYPE html>
<html lang="cn">
  <head>
    <title>MVVM实现 method01.发布订阅模式.PubSub</title>
    <meta name="author" content="Vincent Lau" />
    <meta name="keywords" content="JavaScript,js" />
    <meta name="description" content="JavaScript Basic Guide" />
    <meta charset="utf-8" />
    <meta name="format-detection" content="telephone=no,email=no,date=no,address=no" />
    <meta name="copyright" content="www.godsdebt.com" />

    <!--360浏览器-->
    <meta name="renderer" content="webkit" />
    <!--其它双核浏览器-->
    <meta name="force-rendering" content="webkit" />
    <!--如果安装了GCF，则使用GCF来渲染页面，如果没有安装GCF，则使用最高版本的IE内核进行渲染。-->
    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
    <!-- Mobile Specific -->
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
    <!-- referrer-policy -->
    <meta name="referrer" content="always" />

    <link type="image/x-icon" href="../../img/favicon.ico" rel="shortcut icon" />
    <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto:300,100" />
    <!--
        <link rel="stylesheet" href="plugin/animate/v3.5.2/animate.min.css" />
        <link rel="stylesheet" href="plugin/normalize/v7.0.0/normalize.css" />
        <link rel="stylesheet" href="plugin/vidage/v0.3.5/styles/Vidage.min.css" />
    -->

    <!-- cdnjs加速 -->
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/animate.css/3.5.2/animate.min.css" />
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/7.0.0/normalize.min.css" />
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/Vidage/0.3.5/styles/Vidage.min.css" />

    <link rel="stylesheet" href="//unpkg.com/iview/dist/styles/iview.css" />
    <!--
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/iview/2.0.0-rc.17/styles/iview.css" />
    -->
    <link rel="stylesheet" href="../../css/style.css" />
    <link rel="stylesheet" href="../../css/index.css" />
  </head>

  <body>
    <div class="Vidage">
      <div class="Vidage__image"></div>
      <video id="VidageVideo" class="Vidage__video" preload="metadata" loop autoplay muted>
        <source src="../../video/bg.webm" type="video/webm" />
        <source src="../../video/bg.mp4" type="video/mp4" />
      </video>
      <div class="Vidage__backdrop"></div>
    </div>

    <div class="container flex" id="app">
      <div class="home flex flex-column flex-center">
        <div>
          <div>
            <label class="fcon" for="name">姓名：</label>
            <input id="name" type="text" v-model="name" />
            <button class="bg-blue fcw" v-on-click="change">修改</button>
          </div>
          <div>
            <label class="fcon" for="type">类型：</label>
            <input id="type" type="text" v-model="special.type" />
            <button class="bg-blue fcw" v-on-click="changeType">修改</button>
          </div>
          <div class="fcw">
            <span class="fcon">我是</span>
            <span v-text="name"></span>
            <span v-text="special.type"></span>
          </div>
        </div>
      </div>

      <ul class="menu flex flex-column flex-center">
        <li data-title="Home">发布订阅模式</li>
        <li data-title="Equal">数据劫持模式</li>
        <li data-title="About">脏检测</li>
      </ul>
    </div>

    <footer class="flex">
      <div class="main-icon"></div>
      <div class="copyright">
        Copyright ©2018 Vincent Lau | All Rights Reserved.
      </div>
    </footer>

    <script src="//cdnjs.cloudflare.com/ajax/libs/Vidage/0.3.5/scripts/Vidage.min.js"></script>
    <script src="//cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
    <script src="//unpkg.com/iview/dist/iview.min.js"></script>
    <script src="../../plugin/is/is.min.js"></script>
    <script type="module">
      import PubSub from '../vol001.DesginPattern/DP02.PubSub.发布订阅模式.js';
      new Vidage('#VidageVideo');

      class MVVM {
        constructor (config) {
          this.dom = document.querySelector(config.el);
          this.data = config.data;
          this.method = config.method;

          this._init();
          this._load();
          this._bind();
        }
        _init () {
          this._PubSub = new PubSub();
          this._VTOPIC = 'v-change'; // view改变
          this._MTOPIC = 'm-change'; // model改变

          // 发布订阅
          this._PubSub.subscribe(this._VTOPIC, (k, v) => {
            this._update(k, v);
          });
          this._PubSub.subscribe(this._MTOPIC, (k, v) => {
            // :model表示双向绑定
            let els = this.dom.querySelectorAll('[v-model="' + k + '"]');
            els.forEach(el => {
              let tag = el.tagName.toLowerCase();
              if (tag === 'input' || tag === 'textarea' || tag === 'select') {
                el.value = v;
              } else {
                el.innerHTML = v;
              }
            });

            // :text表示单向绑定文本
            els = this.dom.querySelectorAll('[v-text="' + k + '"]');
            els.forEach(el => {
              el.innerText = v;
            });

            // :html表示单向绑定富文本
            els = this.dom.querySelectorAll('[v-html="' + k + '"]');
            els.forEach(el => {
              el.innerHTML = v;
            });
          });

          // 更新处理
          this.eventHandler = e => {
            let target = e.target || e.srcElemnt;
            let key = target.getAttribute('v-model');
            let value = target.value;
            console.log(e, key, value);
            if (key) {
              // ui层更新
              this._PubSub.publish(this._VTOPIC, key, value);
            }
          };

          // input要监听change， radio checkbox要监听click， select监听change，contenteditable需要判断是否修改 input
          /* from avalon.js (directives/duplex/updateDataEvents.modern.js)
           * 通过绑定事件同步vmodel
           * 总共有三种方式同步视图
           * 1. 各种事件 input, change, click, propertychange, keydown...
           * 2. value属性重写
           * 3. 定时器轮询
           */
          this.dom.addEventListener('keyup', this.eventHandler, false); // need fix: 输入拼音时没有输入完并不更新
          this.dom.addEventListener('change', this.eventHandler, false);
        }
        _update (k, v) {
          // console.log('console.log(this);this.data['+k.split('.').join('"]["')+'] = "'+v+'"')
          eval('console.log(this);this.data["' + k.split('.').join('"]["') + '"] = "' + v + '"');
          this._PubSub.publish(this._MTOPIC, k, v);
        }
        _load (data, pre) {
          // 初始化数据绑定
          if (!data) {
            data = this.data;
          }
          pre = pre ? pre + '.' : '';
          for (let prop in data) {
            if (is.json(data[prop])) {
              this._load(data[prop], pre + prop);
            } else if (is.array(data[prop])) {
            } else {
              this._update(pre + prop, data[prop]);
            }
          }
        }
        _bind () {
          // 初始化事件绑定
          this.dom.addEventListener(
            'click',
            e => {
              let target = e.target || e.srcElemnt;
              let key = target.getAttribute('v-on-click');
              console.log(e, target, key);
              if (key in this.method) {
                this.method[key].call(this);
              }
            },
            false
          );
        }
      }

      var vm = new MVVM({
        el: '#app',
        data: {
          name: '旧名字',
          sex: 1,
          special: {
            type: 1
          }
        },
        method: {
          change: function () {
            this._update('name', '新名字');
            // this.data.name = '新名字'
          }
        }
      });
    </script>
  </body>
</html>
